Controller、Service、Repository,是寫程式時很熟悉的分層。那麼,把它們各自拆成一個服務,就完成微服務了嗎?我會先回到業務來看:它們是不是仍在完成同一件事,而且每次都要一起修改?
Controller、Service、Repository 這幾層,原本就是同一件業務的不同處理步驟。如果拆開之後仍要一起部署,卻多了網路延遲與失敗處理,那就需要再想想,這次拆分帶來了什麼幫助。
我會用一個簡單的問題開始:同一個業務需求進來,拆出來的兩個服務是不是幾乎都要一起改?如果經常如此,就值得回頭檢查那條邊界。
以製造業來說,可以先從訂單、物料、庫存與 EDI 這些熟悉的工作開始整理。各自有哪些資料、規則與生命週期,先談清楚,再來看適合放在哪個模組或服務。
接著,我會對每一項工作確認四件事:資料正確性誰負責、哪些規則會一起改、故障時能不能分開處理,以及是否需要獨立擴展與發佈。拿任兩項工作互相比對,如果四個問題的答案都一樣,就可以先放在同一個範圍內。

圖 Day 07-1:依業務域拆分服務。
因為想保留「先理解公司有哪些工作,再安排哪支程式負責」的順序,圖上才先放業務能力,再往下連到服務,而不是先決定服務數量,再把工作塞進去。每一項能力都要講得出自己的資料是誰的、規則是誰定的。像信用額度檢查,資料在客戶那邊、規則卻由訂單在定,這樣的能力就還不算乾淨的邊界,要先釐清它屬於訂單、屬於客戶,還是該獨立出來。
也可以用故障情境檢查分工。假設庫存服務停了,訂單完全建不起來,就要回頭問業務單位:沒有庫存資料時,訂單真的不能先收嗎?如果答案是不能,這個依賴就是業務本來的規則,分工沒有問題,只是要知道兩者綁在一起。如果答案是可以先收單、之後再確認庫存,訂單服務就應該有降級方式,例如先建單並標記待確認,而不是直接報錯;程式目前完全不讓建單,表示它把兩者綁得比業務要求還緊。這樣推一次,比只看服務名稱更容易看出誰依賴誰、依賴多深。
每個候選服務,我會先替它做一張 context card,把目的、核心實體、寫入權限、API、事件、負責人與 SLO 記下來。其中寫入權限最重要。因為兩個服務都能寫同一張表時,資料改錯了查不出是誰改的,邊界等於沒切,所以每張資料表只能由一個服務負責寫入。訂單服務要改庫存量,不能自己去更新庫存表,要呼叫庫存服務的 API,或發事件讓庫存服務處理。
兩張卡如果出現相同的核心實體名稱,也要一起確認意思。例如倉庫認為貨物離開倉庫才算已出貨,業務認為出貨單開好就算,同一天兩邊報出來的已出貨筆數就會不同。這不是程式問題,是定義沒談好。提早把認定時間寫在卡上,作業上會比較順。
真正要做時,不必一開始就拆成獨立部署的服務,可以先在同一個程式裡把訂單、庫存分成不同的套件,演練這個分工。Spring Modulith 或 ArchUnit 這類工具能把「訂單套件不能直接引用庫存的 repository」寫成測試,有人越界,建置就失敗,邊界不用靠大家記得。在同一個程式裡守不住的邊界,拆開部署也一樣守不住,差別只是原本的直接呼叫變成網路呼叫,多了逾時和重試要處理。所以先確認邊界守得住,再決定要不要獨立部署。
| 取捨 | 這樣選的理由 | 何時要重新評估 |
|---|---|---|
| 初期採較保守的拆分 | 合併容易、拆開難;資訊不足時先少拆 | 某個域改得比其他域頻繁許多,其他域被迫跟著重新部署時 |
| 先以模組驗證邊界 | 邊界錯了可以重構,不必改契約與部署 | 模組已穩定且需要獨立擴展或發佈時 |
| 跨域一律走 API 或事件 | 保留各域調整內部結構的空間 | 效能需求無法由 API 或事件滿足時 |
| 以依賴檢查工具強制執行 | 文件規範無法阻止繞過 | 檢查規則與實際架構決策不一致時 |
一開始不一定能把每條邊界都想得很完整。我會先保守一點,從較少的拆分開始,再看實際變更來調整。這樣也能少一些拆開後又要整合回來的工作。
| 指標 | 計算方式 | 想回答的問題 |
|---|---|---|
| 同步呼叫深度 | 一筆交易串接的同步呼叫層數 | 是否只是把技術分層變成了網路呼叫? |
| 單一需求涉及服務數 | 一個需求平均變更的服務數 | 邊界是否切在會一起變動的位置? |
| 共同發佈比例 | 同一需求內,需同時發佈的服務數/變更服務數 | 服務是否只是名義上獨立? |
| 跨域資料寫入次數 | 違反所有權直接寫入他域的次數 | 邊界規範是否被遵守? |
因為有可能一次大專案就會讓所有服務短暫一起變動,所以這四項數字要持續追蹤,但要看趨勢,不看單次。我自己的提醒門檻有三點:觀察期至少涵蓋三到四次正式發佈、或十到十五個需求;連續兩個觀察期都出現同樣模式才算訊號;某一對服務有超過一半的變更要一起改、一起測、一起上線,就回頭檢查邊界。門檻到了代表該回頭看,不代表要直接合併。
今天先把服務要負責的事情分清楚。接下來,還要確認誰能讀、誰能寫,這份分工才會真的落實在系統裡。下一篇,就來整理資料所有權。
先用模組與 ArchUnit 驗證邊界,再決定是否獨立部署,比先拆成一地網路呼叫務實很多。你列了共同發佈比例與單一需求涉及服務數;實務上會觀察多久、到什麼門檻,才判定兩個服務該合併或重新切界?
我自己的作法,會把觀察門檻整理成三點:
這些門檻對我來說比較像提醒,代表服務邊界可能要回頭看一下,不是數字到了就直接合併。
最後還是要看實際狀況,是業務邏輯切得不對、介面綁太緊、服務拆得太細,還是目前這樣拆,其實比較符合公司的政策、維運方式和後續發展。